<?php
/**
 * Created by PhpStorm.
 * User: xuho
 * Date: 2020-09-02
 * Time: 17:10
 */

namespace extend\payment;


class FuYouPayService
{
    public $domain = '';
    public $privateKey = '';

    // 订单类型：ALIPAY-支付宝，WECHAT-公众号，UNIONPAY-银联二维码，BESTPAY-翼支付
    public $orderType = ['1' => 'WECHAT', '2' => 'ALIPAY', '3' => 'UNIONPAY', '4' => 'BESTPAY'];
    // 支付类型：JSAPI-公众号支付、APP-app 支付、FWC-支付宝服务窗、QQ-QQ 页面支付、QQJSAPI-QQJSAPI、LETPAY-小程序
    public $tradeType = ['1' => 'JSAPI', '2' => 'FWC', '3' => 'APP', '4' => 'QQ', '5' => 'QQJSAPI', '6' => 'LETPAY'];
    public $preType = ['1' => 'WXPA', '2' => 'ALPA'];


    /**
     * 条码支付，商户扫客户
     * @param $request
     * @return mixed
     * @throws \Exception
     */
    public function microOrder($request)
    {
        // 接口类型
        $apiType = 'order_create';

        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/micropay';

        $paramSign['order_type'] = $this->orderType[$request['order_type']];
        $paramSign['goods_des'] = $request['goods_des'];
        $paramSign['order_amt'] = $request['order_amt'];
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];
        // 授权码
        $paramSign['auth_code'] = $request['auth_code'];

        // 非必填数据
        $paramSign['goods_detail'] = $request['goods_detail'] ?? '';
        $paramSign['goods_tag'] = $request['goods_tag'] ?? '';

        // 支付场景：1-条形支付 2-声波支付
        $paramSign['sence'] = $request['sence'] ?? '1';


        // 不参与校验的数据
        $paramAttach['reserved_sub_appid'] = $request['reserved_sub_appid'] ?? '';
        $paramAttach['reserved_limit_pay'] = $request['reserved_limit_pay'] ?? '';
        $paramAttach['reserved_expire_minute'] = $request['reserved_expire_minute'] ?? '';
        $paramAttach['reserved_fy_term_id'] = $request['reserved_fy_term_id'] ?? '';
        $paramAttach['reserved_fy_term_type'] = $request['reserved_fy_term_type'] ?? '';
        $paramAttach['reserved_fy_term_sn'] = $request['reserved_fy_term_sn'] ?? '';

        $response = $this->sendRequest($urlApi, $paramSign, $paramAttach);

        return $this->afterRequest($response, $apiType);

    }

    /**
     * 扫码支付，客户扫商户，主扫
     * @param array $request
     * @return mixed
     * @throws \Exception
     */
    public function codeOrder($request = [])
    {
        // 接口类型
        $apiType = 'order_create';

        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/preCreate';

        // 必填数据
        $paramSign['order_type'] = $this->orderType[$request['order_type']];
        $paramSign['goods_des'] = $request['goods_des'];
        $paramSign['order_amt'] = $request['order_amt'];
        $paramSign['notify_url'] = $request['notify_url'];
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];

        // 非必填数据
        $paramSign['goods_detail'] = $request['goods_detail'] ?? '';
        $paramSign['goods_tag'] = $request['goods_tag'] ?? '';

        // 不参与校验的数据
        $paramAttach['reserved_sub_appid'] = $request['reserved_sub_appid'] ?? '';
        $paramAttach['reserved_limit_pay'] = $request['reserved_limit_pay'] ?? '';
        $paramAttach['reserved_expire_minute'] = $request['reserved_expire_minute'] ?? '';
        $paramAttach['reserved_fy_term_id'] = $request['reserved_fy_term_id'] ?? '';
        $paramAttach['reserved_fy_term_type'] = $request['reserved_fy_term_type'] ?? '';
        $paramAttach['reserved_fy_term_sn'] = $request['reserved_fy_term_sn'] ?? '';
        $response = $this->sendRequest($urlApi, $paramSign, $paramAttach);

        return $this->afterRequest($response, $apiType);
    }

    /**
     * 统一下订单，服务窗模式
     * 自定义生成订单方式
     * @param $request
     * @return mixed
     * @throws \Exception
     */
    public function unifiedOrder($request)
    {
        // 接口类型
        $apiType = 'order_create';

        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/wxPreCreate';

        // 必填数据
        $paramSign['goods_des'] = $request['goods_des'];
        $paramSign['order_amt'] = $request['order_amt'];
        $paramSign['notify_url'] = $request['notify_url'];
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];
        $paramSign['trade_type'] = $this->tradeType[$request['trade_type']];


        // 非必填数据
        $paramSign['openid'] = $request['openid'] ?? '';
        $paramSign['sub_openid'] = $request['sub_openid'] ?? '';
        $paramSign['sub_appid'] = $request['sub_appid'] ?? '';

        $paramSign['goods_detail'] = $request['goods_detail'] ?? '';
        $paramSign['goods_tag'] = $request['goods_tag'] ?? '';
        $paramSign['product_id'] = $request['product_id'] ?? '';
        $paramSign['limit_pay'] = $request['limit_pay'] ?? '';


        // 不参与校验的数据
        $paramAttach['reserved_expire_minute'] = $request['reserved_expire_minute'] ?? '';
        $paramAttach['reserved_fy_term_id'] = $request['reserved_fy_term_id'] ?? '';
        $paramAttach['reserved_fy_term_type'] = $request['reserved_fy_term_type'] ?? '';
        $paramAttach['reserved_txn_bonus'] = $request['Reserved_txn_bonus'] ?? '';
        $paramAttach['reserved_fy_term_sn'] = $request['reserved_fy_term_sn'] ?? '';


        $response = $this->sendRequest($urlApi, $paramSign, $paramAttach);

        return $this->afterRequest($response, $apiType);
    }

    /**
     * 订单查询
     * @param $request
     * @return mixed
     * @throws \Exception
     */
    public function orderQuery($request)
    {
        // 接口类型
        $apiType = 'order';

        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/commonQuery';

        // 必填数据
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];
        $paramSign['order_type'] = $this->orderType[$request['order_type']];

        $response = $this->sendRequest($urlApi, $paramSign);

        return $this->afterRequest($response, $apiType);
    }

    /**
     * 退款订单
     * @param $request
     * @return mixed
     * @throws \Exception
     */
    public function orderRefund($request)
    {
        // 接口类型
        $apiType = 'order';

        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/commonRefund';

        // 必填数据
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];
        $paramSign['order_type'] = $this->orderType[$request['order_type']];
        $paramSign['refund_order_no'] = $request['refund_order_no'];
        $paramSign['total_amt'] = $request['total_amt'];
        $paramSign['refund_amt'] = $request['refund_amt'];

        // 非必填数据
        $paramSign['operator_id'] = $param['operator_id'] ?? '';

        // 不参与校验的数据
        $paramAttach['reserved_fy_term_id'] = $param['reserved_fy_term_id'] ?? '';

        $response = $this->sendRequest($urlApi, $paramSign, $paramAttach);

        return $this->afterRequest($response, $apiType);
    }

    /**
     * 关闭订单
     * 只支持未支付的订单，并且要在产生订单的 5分钟之后才能取消
     * @param $request
     * @return mixed
     * @throws \Exception
     */
    public function orderClose($request)
    {
        // 接口类型
        $apiType = 'order';

        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/closeorder';

        // 必填数据
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];
        $paramSign['order_type'] = $this->orderType[$request['order_type']];

        // 非必填数据
        $paramSign['sub_appid'] = $request['sub_appid'] ?? '';

        $response = $this->sendRequest($urlApi, $paramSign);

        return $this->afterRequest($response, $apiType);
    }

    /**
     * 撤销订单
     * 只支持条码支付撤销
     * @param $request
     * @return mixed
     * @throws \Exception
     */
    public function orderCancel($request)
    {
        // 接口类型
        $apiType = 'order';
        // 获取支付参数
        $paramSign = $this->beforeRequest($request, $apiType);
        // 接口地址
        $urlApi = '/cancelorder';

        // 必填数据
        $paramSign['mchnt_order_no'] = $request['mchnt_order_no'];
        $paramSign['order_type'] = $this->orderType[$request['order_type']];
        $paramSign['cancel_order_no'] = $request['cancel_order_no'];

        // 非必填数据
        $paramSign['operator_id'] = $param['operator_id'] ?? '';

        // 不参与校验的数据
        $paramAttach['reserved_fy_term_id'] = $param['reserved_fy_term_id'] ?? '';

        $response = $this->sendRequest($urlApi, $paramSign, $paramAttach);

        return $this->afterRequest($response, $apiType);
    }

    public function beforeRequest($request = [], $type = 'order')
    {
        $this->domain = $request['domain'];
        $this->privateKey = $request['private_key'];

        // 机构号
        $paramSign['ins_cd'] = $request['ins_cd'];
        // 商户号
        $paramSign['mchnt_cd'] = $request['mchnt_cd'];
        // 随机字符串
        $paramSign['random_str'] = nonceStr();
        // 版本
        $paramSign['version'] = $request['version'];
        // 终端号(没有真实终端号统一填88888888)
        $paramSign['term_id'] = $request['term_id'];

        // 判断是否为创建订单接口
        if ($type == 'order_create') {
            // 附加数据
            $paramSign['addn_inf'] = '';
            // 交易时间
            $paramSign['txn_begin_ts'] = date('YmdHis', time());
            // 货币类型
            $paramSign['curr_type'] = $request['curr_type'];
            // ip 地址
            $paramSign['term_ip'] = $request['term_ip'];
        }

        return $paramSign;
    }


    /**
     * 服务模式下的发送请求
     * @param $url
     * @param $paramSign
     * @param array $paramAttach
     * @return string
     */
    public function sendRequest($url, $paramSign, $paramAttach = [])
    {
        // 链接
        $url = $this->domain . $url;
        $privateKey = $this->privateKey;

        // 改变编码，传成GBK
        $paramSign = array2GBK($paramSign);

        // RSAwithMD5+base64加密后得到的sign
        $paramSign['sign'] = privateSign(sortAndCombine($paramSign), $privateKey, 'RSA2', 'MD5');

//var_dump($paramSign);

        // 判断是否有附加数据
        if (!empty($paramAttach)) {
            $paramAttach = array2GBK($paramAttach);
            $paramSign = array_merge($paramSign, $paramAttach);
        }

        $paramSign = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>" . arrayToXml2($paramSign) . "</xml>";

        // 经过两次urlencode()之后的字符串
        $paramSign = 'req=' . urlencode(urlencode($paramSign));

        // 过滤url
        $url = str_replace(' ', '+', $url);

        //返回的xml字符串
        $response = urldecode(postRequest($url, $paramSign));
        return $response;
    }


    /**
     * 返回结果
     * @param $response
     * @param string $response_type
     *  共4种类型 ： order_create、order、withdraw、settle
     * @return mixed
     * @throws \Exception
     */
    public function afterRequest($response, $response_type)
    {
        $response = xml2array($response);

        if (strpos($response_type, 'order') !== false) {
            // 订单类型
            if ($response['result_code'] == '000000') {
                $result['status'] = '1';
                $result['msg'] = 'success';
                $result['data'] = $response;
            } else {
                $result['status'] = '0';
                $result['msg'] = $response['result_msg'];
            }
        } else if ($response_type == 'withdraw') {
            // 提现类型
            if ($response['result_code'] == 'SUCCESS') {
                $result['status'] = '1';
                $result['msg'] = 'success';
                $result['data'] = $response;
            } else {
                $result['status'] = '0';
                $result['msg'] = $response['return_msg'];
            }
        } else {
            $result['status'] = '1';
            $result['msg'] = 'success';
            $result['data'] = $response;
        }
        return $result;
    }
}